package com.waa.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.waa.gulimall.common.to.mq.OrderTo;
import com.waa.gulimall.common.to.mq.SeckilOrderTo;
import com.waa.gulimall.common.utils.R;
import com.waa.gulimall.common.vo.MemberRespVo;
import com.waa.gulimall.order.constant.OrderConstant;
import com.waa.gulimall.order.dao.OrderItemDao;
import com.waa.gulimall.order.entity.OrderItemEntity;
import com.waa.gulimall.order.enume.OrderStatusEnum;
import com.waa.gulimall.order.fegin.CartFeginService;
import com.waa.gulimall.order.fegin.MemberFeignServic;
import com.waa.gulimall.order.fegin.ProductFeignService;
import com.waa.gulimall.order.fegin.WmsFeignService;
import com.waa.gulimall.order.interceptor.LoginUserInterceptor;
import com.waa.gulimall.order.service.OrderItemService;
import com.waa.gulimall.order.vo.*;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.waa.gulimall.common.utils.PageUtils;
import com.waa.gulimall.common.utils.Query;

import com.waa.gulimall.order.dao.OrderDao;
import com.waa.gulimall.order.entity.OrderEntity;
import com.waa.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> threadLocal = new ThreadLocal<OrderSubmitVo>();
    @Autowired
    private MemberFeignServic memberFeignServic;
    @Autowired
    private OrderItemDao orderItemDao;
    @Autowired
    private CartFeginService cartFeginService;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private WmsFeignService wmsFeignService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirm() throws ExecutionException, InterruptedException {
        final OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        //异步任务执行之前先共享数据
        final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //1.远程查询所有的收货地址
        final MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        final CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
            //在主线程中拿到原来的数据，再父线程里面共享RequestContextHolder
            //只有共享拦截器中才会有数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            final List<MemberAddressVo> address = memberFeignServic.getAddress(memberRespVo.getId());
            orderConfirmVo.setAddress(address);
        }, threadPoolExecutor);

        //2.远程查询购物车所有选中的购物项
        final CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
            //在主线程中拿到原来的数据，再父线程里面共享RequestContextHolder
            RequestContextHolder.setRequestAttributes(requestAttributes);
            final List<OrderItemVo> currentUserCartItems = cartFeginService.getCurrentUserCartItems();
            orderConfirmVo.setItem(currentUserCartItems);
        }, threadPoolExecutor).thenRunAsync(() -> {
            final List<OrderItemVo> item = orderConfirmVo.getItem();
            final List<Long> skuIds = item.stream().map(it -> {
                return it.getSkuId();
            }).collect(Collectors.toList());
            final R skuHasStock = wmsFeignService.getSkuHasStock(skuIds);
            final List<SkuHasStockVo> data = (List<SkuHasStockVo>) skuHasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
            });
            if (data != null) {
                final Map<Long, Boolean> stockResult = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                orderConfirmVo.setStocks(skuHasStock);
            }
        }, threadPoolExecutor);

        //fegin在远程调用前要构造请求，调用很多的拦截器
        //RequestInterceptor interceptor : RequestInterceptors

        //CompletableFuture.runAsync()
        //3.查询用户积分
        final Integer integration = memberRespVo.getIntegration();//积分
        orderConfirmVo.setIntegration(integration);

        //4.其他数据自动计算
        //TODO 防重令牌
        CompletableFuture.allOf(getAddressFuture, cartFuture).get();
        final String token = UUID.randomUUID().toString().replace("-", "");
        stringRedisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 30, TimeUnit.MINUTES);
        orderConfirmVo.setOrderToken(token);
        return orderConfirmVo;
    }


    @Override
    @Transactional
    @GlobalTransactional
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        threadLocal.set(orderSubmitVo);
        final SubmitOrderResponseVo submitOrderResponseVo = new SubmitOrderResponseVo();
        final MemberRespVo memberInfo = LoginUserInterceptor.loginUser.get();
        submitOrderResponseVo.setCode(0);

        final String orderToken = orderSubmitVo.getOrderToken();//页面提交过来的令牌
        //final String redisToken = stringRedisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberInfo.getId());

        // 验证令牌【令牌的对比和删除必须保证原子性】
        // 因此使用redis中脚本来进行验证并删除令牌
        // 0【删除失败/验证失败】 1【删除成功】
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        final Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberInfo.getId()), orderToken);//原子删除

        if (result == 0L) {
            //令牌验证失败
            // 验证失败直接返回结果
            submitOrderResponseVo.setCode(1);
            return submitOrderResponseVo;
        } else {
            //验证成功
            // 下单 创建订单、验证令牌、验证价格、验证库存
            // 1、创建订单、订单项信息

            OrderCreateTo order = createOrder();
            //验价
            final BigDecimal payAmount = order.getOrder().getPayAmount();
            final BigDecimal payPrice = orderSubmitVo.getPayPrice();
            if (Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){
                //金额对比
                //保存订单
                saveOrder(order);
                //锁定库存
                final WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
                wareSkuLockVo.setOrderSn(order.getOrder().getOrderSn());
                // 准备好商品项
                List<OrderItemVo> lock = order.getOrderItems().stream().map(orderItemEntity -> {
                    OrderItemVo orderItemVo = new OrderItemVo();
                    // 商品购买数量
                    orderItemVo.setCount(orderItemEntity.getSkuQuantity());
                    // skuid 用来查询商品信息
                    orderItemVo.setSkuId(orderItemEntity.getSkuId());
                    // 商品标题
                    orderItemVo.setTitle(orderItemEntity.getSkuName());
                    return orderItemVo;
                }).collect(Collectors.toList());
                final R r = wmsFeignService.orderLockStock(wareSkuLockVo);
                if (r.getCode()==0){
                    //锁定成功
                    //TODO 订单创建成功，开始发消息给MQ
                    rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder());
                    return submitOrderResponseVo;
                }else {
                    //锁定失败
                    submitOrderResponseVo.setCode(3);
                    return submitOrderResponseVo;
                }

            }else {
                submitOrderResponseVo.setCode(2);
                return submitOrderResponseVo;
            }

        }

//        if (orderToken!=null && orderToken.equals(redisToken)){
//          //令牌验证通过
//        }else {
//            //不通过
//
//        }
    }

    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        final OrderEntity order_sn = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return order_sn;
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {
         //查询当前订单的最新状态
        final OrderEntity entity = this.getById(orderEntity.getId());
        if (entity.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()){
            //关单
            final OrderEntity update = new OrderEntity();
            update.setId(entity.getId());
            update.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(update);
            final OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(entity,orderTo);
            //发送给MQ一个消息，防止订单由于网络问题而影响库存
            try {
                //TODO 保证消息一定会发出去，每一个消息都可以做好日志记录（给数据库保存每一个消息的详细信息）
                //TODO 定期扫描数据库，将失败的消息重新发一次
            }catch (Exception e){
                //TODO 将没发送出去的消息，进行重试发送
            }
            rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderTo);
        }
    }

    @Override
    public void createSeckillOrder(SeckilOrderTo seckilOrderTo) {
        //TODO 保存订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckilOrderTo.getOrderSn());
        orderEntity.setMemberId(seckilOrderTo.getMemberId());

        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        final BigDecimal multiply = seckilOrderTo.getSeckillPrice().multiply(new BigDecimal("" + seckilOrderTo.getNum()));
        orderEntity.setPayAmount(multiply);
        this.save(orderEntity);
        //保存订单项信息
        final OrderItemEntity itemEntity = new OrderItemEntity();
        itemEntity.setOrderSn(seckilOrderTo.getOrderSn());
        itemEntity.setRealAmount(multiply);
        //TODO 获取sku的详细信息进行设置productFeignService.getSpuInfoBySkuId()
        itemEntity.setSkuQuantity(seckilOrderTo.getNum());
        orderItemService.save(itemEntity);
    }

    /**
     * 保存订单数据
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        final OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        this.save(orderEntity);
        final List<OrderItemEntity> orderItems = order.getOrderItems();
        orderItemService.saveBatch(orderItems);

    }

    private OrderCreateTo createOrder() {
        final OrderCreateTo orderCreateTo = new OrderCreateTo();
        //生成订单号
        final String orederSN = IdWorker.getTimeId();
        //构建订单
        final OrderEntity orderEntity = bulidOrder(orederSN);
        //获取所有的订单项
        final List<OrderItemEntity> orderItemEntities = buildOrderItems();
         //计算价格相关
        computePrice(orderEntity,orderItemEntities);

        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItems(orderItemEntities);
        return orderCreateTo;
    }

    private OrderEntity computePrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities) {
        //订单价格先关的
        // 1、定义好相关金额，然后遍历购物项进行计算
        // 总价格
        BigDecimal total = new BigDecimal("0");
        //相关优惠信息
        // 优惠卷抵扣金额
        BigDecimal coupon = new BigDecimal("0");
        // 积分优惠金额
        BigDecimal integration = new BigDecimal("0");
        // 促销优惠金额
        BigDecimal promotion = new BigDecimal("0");
        // 积分
        BigDecimal gift = new BigDecimal("0");
        // 成长值
        BigDecimal growth = new BigDecimal("0");

        // 遍历订单项将所有的优惠信息进行相加
        for (OrderItemEntity itemEntity : itemEntities) {
            coupon = coupon.add(itemEntity.getCouponAmount()); // 优惠卷抵扣
            integration = integration.add(itemEntity.getIntegrationAmount()); // 积分优惠分解金额
            promotion = promotion.add(itemEntity.getPromotionAmount()); // 商品促销分解金额
            gift = gift.add(new BigDecimal(itemEntity.getGiftIntegration().toString())); // 赠送积分
            growth = growth.add(new BigDecimal(itemEntity.getGiftGrowth())); // 赠送成长值
            total = total.add(itemEntity.getRealAmount()); //优惠后的总金额
        }

        // 2、设置订单金额
        // 订单总金额
        orderEntity.setTotalAmount(total);
        // 应付总额 = 订单总额 + 运费信息
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        // 促销优化金额（促销价、满减、阶梯价）
        orderEntity.setPromotionAmount(promotion);
        // 优惠券抵扣金额
        orderEntity.setCouponAmount(coupon);

        // 3、设置积分信息
        // 订单购买后可以获得的成长值
        orderEntity.setGrowth(growth.intValue());
        // 积分抵扣金额
        orderEntity.setIntegrationAmount(integration);
        // 可以获得的积分
        orderEntity.setIntegration(gift.intValue());
        // 删除状态【0->未删除；1->已删除】
        orderEntity.setDeleteStatus(0);
        return orderEntity;
    }

    /**
     * 构建所有的订单项数据
     *
     * @return
     */
    private List<OrderItemEntity> buildOrderItems() {
        //最后确定每个购物项的价格
        final List<OrderItemVo> currentUserCartItems = cartFeginService.getCurrentUserCartItems();
        if (currentUserCartItems != null && currentUserCartItems.size() > 0) {
            final List<OrderItemEntity> itemEntities = currentUserCartItems.stream().map(cartItem -> {
                OrderItemEntity itemEntity = buildOrderItem(cartItem);
                return itemEntity;
            }).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 构建每个订单项
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
        OrderItemEntity itemEntity = new OrderItemEntity();
        // 1、根据skuid查询关联的spuinfo信息
        Long skuId = cartItem.getSkuId();
        R spuinfo = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo spuInfoVo = (SpuInfoVo) spuinfo.getData(new TypeReference<SpuInfoVo>() {
        });
        // 2、设置商品项spu信息
        // 品牌信息
        itemEntity.setSpuBrand(spuInfoVo.getBrandId().toString());
        // 商品分类信息
        itemEntity.setCategoryId(spuInfoVo.getCatalogId());
        // spuid
        itemEntity.setSpuId(spuInfoVo.getId());
        // spu_name 商品名字
        itemEntity.setSpuName(spuInfoVo.getSpuName());

        // 3、设置商品sku信息
        // skuid
        itemEntity.setSkuId(skuId);
        // 商品标题
        itemEntity.setSkuName(cartItem.getTitle());
        // 商品图片
        itemEntity.setSkuPic(cartItem.getImage());
        // 商品sku价格
        itemEntity.setSkuPrice(cartItem.getPrice());
        // 商品属性以 ; 拆分
        String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
        itemEntity.setSkuAttrsVals(skuAttr);
        // 商品购买数量
        itemEntity.setSkuQuantity(cartItem.getCount());

        // 4、设置商品优惠信息【不做】
        // 5、设置商品积分信息
        // 赠送积分 移弃小数值
        itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());
        // 赠送成长值
        itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());

        // 6、订单项的价格信息
        // 这里需要计算商品的分解信息
        // 商品促销分解金额
        itemEntity.setPromotionAmount(new BigDecimal("0"));
        // 优惠券优惠分解金额
        itemEntity.setCouponAmount(new BigDecimal("0"));
        // 积分优惠分解金额
        itemEntity.setIntegrationAmount(new BigDecimal("0"));
        // 商品价格乘以商品购买数量=总金额(未包含优惠信息)
        BigDecimal origin = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
        // 总价格减去优惠卷-积分优惠-商品促销金额 = 总金额
        origin.subtract(itemEntity.getPromotionAmount())
                .subtract(itemEntity.getCouponAmount())
                .subtract(itemEntity.getIntegrationAmount());
        // 该商品经过优惠后的分解金额
        itemEntity.setRealAmount(origin);
        return itemEntity;
    }

    private OrderEntity bulidOrder(String orederSN) {

        final OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orederSN);
        orderEntity.setMemberId(LoginUserInterceptor.loginUser.get().getId());
        //获取收货地址信息
        final R fare = wmsFeignService.getFare(threadLocal.get().getAddrId());
        FareVo fareResp = (FareVo) fare.getData(new TypeReference<FareVo>() {
        });
        orderEntity.setFreightAmount(fareResp.getFare());

        //设置收货人信息
        orderEntity.setReceiverCity(fareResp.getAddress().getCity());
        orderEntity.setReceiverDetailAddress(fareResp.getAddress().getDetailAddress());
        orderEntity.setReceiverName(fareResp.getAddress().getName());
        orderEntity.setReceiverPhone(fareResp.getAddress().getPhone());
        orderEntity.setReceiverPostCode(fareResp.getAddress().getPostCode());
        orderEntity.setReceiverProvince(fareResp.getAddress().getProvince());
        orderEntity.setReceiverRegion(fareResp.getAddress().getRegion());
        return orderEntity;
    }

}